﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive;
using System.Windows.Forms;

using Rsdn.Framework.Formatting;
using Rsdn.Janus.Framework;
using Rsdn.Janus.ObjectModel;
using Rsdn.SmartApp;

namespace Rsdn.Janus
{
    /// <summary>
    /// Обработчик команд сообщений форума.
    /// </summary>
    [CommandTarget]
    internal sealed class ForumMessageCommandTarget : CommandTarget
    {
        public ForumMessageCommandTarget(IServiceProvider serviceProvider)
            : base(serviceProvider)
        {
        }

        [CommandExecutor("Janus.Forum.AddForumMessageToFavorites")]
        public void ExecuteAddForumMessageToFavorites(
            ICommandContext context, int? messageId)
        {
            var manager = context.GetRequiredService<IFavoritesManager>();
            var activeMsg = ForumMessageCommandHelper.GetMessage(context, messageId);

            using (var selForm = new FavoritesSelectFolderForm(context, manager.RootFolder, true))
            {
                selForm.Comment = activeMsg.Subject;

                var windowParent = context.GetRequiredService<IUIShell>().GetMainWindowParent();

                if (selForm.ShowDialog(windowParent) == DialogResult.OK)
                {
                    var folder = selForm.SelectedFolder ?? manager.RootFolder;
                    if (!manager.AddMessageLink(activeMsg.ID, selForm.Comment, folder))
                    {
                        // сообщения уже есть в разделе
                        MessageBox.Show(
                            windowParent,
                            SR.Favorites.ItemExists.FormatStr(activeMsg.ID, folder.Name),
                            ApplicationInfo.ApplicationName,
                            MessageBoxButtons.OK,
                            MessageBoxIcon.Information);
                    }
                }
            }
        }

        [CommandExecutor("Janus.Forum.CopyMessageAddress")]
        public void ExecuteCopyMessageAddress(ICommandContext context, int? messageId)
        {
            IMsg msg = ForumMessageCommandHelper.GetMessage(context, messageId);
            string url = SiteUrlHelper.GetMessageUrl(msg.ID);
            ClipboardHelper.CopyUrl(url, msg.Subject);
        }

        [CommandExecutor("Janus.Forum.CopyMessageAuthorAddress")]
        public void ExecuteCopyMessageAuthorAddress(ICommandContext context, int? messageId)
        {
            IMsg msg = ForumMessageCommandHelper.GetMessage(context, messageId);
            string url = SiteUrlHelper.GetUserProfileUrl(msg.UserID);
            ClipboardHelper.CopyUrl(url, msg.UserNick);
        }

        [CommandExecutor("Janus.Forum.OpenMessageInJBrowser")]
        public void ExecuteOpenMessageInJBrowser(ICommandContext context, int? messageId)
        {
            int id = ForumMessageCommandHelper.GetMessageId(context, messageId);
            string url = JanusProtocolDispatcher.FormatURI(JanusProtocolResourceType.Message, id.ToString());

            context.OpenUrlInBrowser(url, UrlBehavior.InternalBrowser);
        }

        [CommandExecutor("Janus.Forum.OpenMessageOnRsdn")]
        public void ExecuteOpenMessageOnRsdn(ICommandContext context, int? messageId)
        {
            int id = ForumMessageCommandHelper.GetMessageId(context, messageId);
            string url = SiteUrlHelper.GetMessageUrl(id);
            context.OpenUrlInBrowser(url);
        }

        [CommandExecutor("Janus.Forum.OpenMessageRatingOnRsdn")]
        public void ExecuteOpenMessageRatingOnRsdn(ICommandContext context, int? messageId)
        {
            int id = ForumMessageCommandHelper.GetMessageId(context, messageId);
            string url = SiteUrlHelper.GetRatingUrl(id);
            context.OpenUrlInBrowser(url);
        }


        [CommandExecutor("Janus.Forum.Moderating")]
        public void ExecuteModerating(ICommandContext context, int? messageId)
        {
            int id = ForumMessageCommandHelper.GetMessageId(context, messageId);

            using (var moderatingForm = new ModeratingForm(context, id))
            {
                IUIShell uiShell = context.GetRequiredService<IUIShell>();
                moderatingForm.ShowDialog(uiShell.GetMainWindowParent());
            }
        }

        [CommandStatusGetter("Janus.Forum.Moderating")]
        public CommandStatus QueryModeratingStatus(ICommandContext context, int? messageId)
        {
            Func<bool> condition = () =>
                                 {
                                     var id = ForumMessageCommandHelper.GetMessageId(context, messageId);
                                     using (IJanusDataContext mgr = context.CreateDBContext())
                                     {
                                         return mgr.Moderatorials().Any(m => m.MessageID == id);
                                     }
                                 };

            CommandStatus commandStatus = QueryMessageCommandStatus(context, messageId);

            return commandStatus.DisabledIfNot(condition);
        }

        [CommandExecutor("Janus.Forum.OpenModeratingOnRsdn")]
        public void ExecuteOpenModeratingOnRsdn(ICommandContext context, int? messageId)
        {
            int id = ForumMessageCommandHelper.GetMessageId(context, messageId);
            string url = SiteUrlHelper.GetSelfModerateUrl(id);
            context.OpenUrlInBrowser(url);
        }

        [CommandExecutor("Janus.Forum.ShowMessageRating")]
        public void ExecuteShowMessageRating(ICommandContext context, int? messageId)
        {
            int id = ForumMessageCommandHelper.GetMessageId(context, messageId);
            string url = SiteUrlHelper.GetRatingUrl(id);
            context.OpenUrlInBrowser(url);
        }


        [CommandExecutor("Janus.Forum.ReplyMessage")]
        public void ExecuteReplyMessage(ICommandContext context, int? messageId)
        {
            IMsg msg = ForumMessageCommandHelper.GetMessage(context, messageId);

            string messageBody = DatabaseManager.GetMessageBody(context, msg.ID);

            var messageInfo = new MessageInfo(
                msg.ForumID,
                msg.ID,
                msg.Subject,
                Format.Forum.GetEditMessage(messageBody, msg.UserNick)
                );

            MessageEditor.EditMessage(ServiceProvider, MessageFormMode.Reply, messageInfo);
        }

        [CommandStatusGetter("Janus.Forum.ReplyMessage")]
        public CommandStatus QueryReplyMessageStatus(ICommandContext context, int? messageId)
        {
            Func<bool> condition = () => !ForumMessageCommandHelper.GetMessage(context, messageId).Closed;
            return QueryMessageCommandStatus(context, messageId).DisabledIfNot(condition);
        }


        [CommandStatusGetter("Janus.Forum.AddForumMessageToFavorites")]
        [CommandStatusGetter("Janus.Forum.CopyMessageAddress")]
        [CommandStatusGetter("Janus.Forum.CopyMessageAuthorAddress")]
        [CommandStatusGetter("Janus.Forum.OpenMessageInJBrowser")]
        [CommandStatusGetter("Janus.Forum.OpenMessageOnRsdn")]
        [CommandStatusGetter("Janus.Forum.OpenMessageRatingOnRsdn")]
        [CommandStatusGetter("Janus.Forum.OpenModeratingOnRsdn")]
        [CommandStatusGetter("Janus.Forum.ShowMessageRating")]
        public CommandStatus QueryMessageCommandStatus(ICommandContext context, int? messageId)
        {
            return ForumMessageCommandHelper.GetSingleMessageCommandStatus(context, messageId);
        }


        [CommandExecutor("Janus.Forum.RateMessage")]
        public void ExecuteRateMessage(ICommandContext context, int[] messageIds, MessageRates rate)
        {
            using (var rateForm = new RateForm(context, rate))
            {
                IUIShell uiShell = context.GetRequiredService<IUIShell>();

                if (rateForm.ShowDialog(uiShell.GetMainWindowParent()) == DialogResult.Yes)
                {
                    IEnumerable<int> ids = ForumMessageCommandHelper.GetMessageIds(context, messageIds);
                    IOutboxManager outboxManager = context.GetRequiredService<IOutboxManager>();

                    Action<int> action = msgId =>
                                         outboxManager
                                             .RateMarks
                                             .Add(msgId, rate);

                    ids.ForEach(action);
                }
            }
        }

        [CommandStatusGetter("Janus.Forum.RateMessage")]
        public CommandStatus QueryRateMessageStatus(ICommandContext context, int[] messageIds)
        {
            Func<bool> condition = () => !ForumMessageCommandHelper.GetMessages(context, messageIds)
                                        .Any(msg => msg.UserID == Config.Instance.SelfId);

            return QueryMessagesCommandStatus(context, messageIds).DisabledIfNot(condition);
        }


        [CommandExecutor("Janus.Forum.RegetTopic")]
        public void ExecuteRegetTopic(ICommandContext context, int[] messageIds)
        {
            IEnumerable<IMsg> messages = ForumMessageCommandHelper.GetMessages(context, messageIds);
            foreach (var msg in messages)
            {
                IOutboxManager outboxManager = context.GetOutboxManager();
                IDownloadTopicCollection downloadTopics = outboxManager.DownloadTopics;
                downloadTopics.Add(SR.Forum.DownloadTopicRegetSource, msg.ID, msg.Subject);
            }
        }

        [CommandStatusGetter("Janus.Forum.RegetTopic")]
        public CommandStatus QueryMessagesCommandStatus(ICommandContext context, int[] messageIds)
        {
            return ForumMessageCommandHelper.GetMultipleMessagesCommandStatus(context, messageIds);
        }


        [CommandExecutor("Janus.Forum.SetTopicReadMark")]
        public void ExecuteSetTopicReadMark(ICommandContext context, int[] messageIds, bool isRead)
        {
            if (Features.Instance.ActiveFeature is Forum)
            {
                ForumDummyForm.Instance.StopMarkTimer();
            }

            IEnumerable<IMsg> msgs = ForumMessageCommandHelper.GetMessages(context, messageIds);
            IEnumerable<MsgBase> messages = msgs
                .Select(msg => msg.Topic)
                .Cast<MsgBase>();

            ForumHelper.MarkMsgRead(context, messages, isRead, true);
        }

        [CommandStatusGetter("Janus.Forum.SetTopicReadMark")]
        public CommandStatus QuerySetTopicReadMarkStatus(ICommandContext context, int[] messageIds, bool isRead)
        {
            CommandStatus commandStatus = QueryMessagesCommandStatus(context, messageIds);
            Func<bool> condition = () => ForumMessageCommandHelper.GetMessages(context, messageIds)
                                       .Select(msg => msg.Topic)
                                       .Any(msg => msg.CanSetMessageReadMark(isRead, true));

            CommandStatus markStatus = commandStatus.DisabledIfNot(condition);

            return markStatus;
        }


        [CommandExecutor("Janus.Forum.SetMessagesReadMark")]
        public void ExecuteSetMessageReadMark(ICommandContext context, int[] messageIds, bool isRead, bool markChilds)
        {
            if (Features.Instance.ActiveFeature is Forum)
                ForumDummyForm.Instance.StopMarkTimer();

            ForumHelper.MarkMsgRead(
               context,
               ForumMessageCommandHelper.GetMessages(context, messageIds).Cast<MsgBase>(),
               isRead,
               markChilds);
        }

        [CommandStatusGetter("Janus.Forum.SetMessagesReadMark")]
        public CommandStatus QuerySetMessagesReadMarkStatus(ICommandContext context, int[] messageIds, bool isRead, bool markChilds)
        {
            CommandStatus commandStatus = QueryMessagesCommandStatus(context, messageIds);
            Func<bool> condition = () => ForumMessageCommandHelper.GetMessages(context, messageIds)
                                       .Any(msg => msg.CanSetMessageReadMark(isRead, markChilds));

            CommandStatus markStatus = commandStatus.DisabledIfNot(condition);
            return markStatus;
        }


        [CommandStatusSubscriber("Janus.Forum.SetMessagesReadMark")]
        [CommandStatusSubscriber("Janus.Forum.SetTopicReadMark")]
        public IDisposable SubscribeReadMarkCommandStatusChanged(IServiceProvider provider, Action handler)
        {
            IEventBroker service = provider.GetRequiredService<IEventBroker>();
            Action<ForumEntryChangedEventArgs> onNext = args =>
                                {
                                    if ((args.ChangeType & ForumEntryChangeType.ReadMark) != 0)
                                    {
                                        handler();
                                    }
                                };

            IObserver<ForumEntryChangedEventArgs> action = Observer.Create<ForumEntryChangedEventArgs>(onNext);
            return service.Subscribe(ForumEventNames.AfterForumEntryChanged, action);
        }


        [CommandExecutor("Janus.Forum.SetMessagesRepliesAutoReadMark")]
        public void ExecuteSetMessagesRepliesAutoReadMark(
            ICommandContext context, int[] messageIds, bool isEnabled)
        {
            ForumHelper.SetMessageRepliesAutoReadMark(
                context,
                ForumMessageCommandHelper.GetMessages(context, messageIds).Cast<MsgBase>(),
                isEnabled);
        }

        [CommandStatusGetter("Janus.Forum.SetMessagesRepliesAutoReadMark")]
        public CommandStatus QuerySetMessagesRepliesAutoReadMarkStatus(
            ICommandContext context, int[] messageIds, bool isEnabled)
        {
            Func<bool> condition = () => ForumMessageCommandHelper
                                       .GetMessages(context, messageIds)
                                       .Any(msg => msg.ReadReplies != isEnabled);

            CommandStatus commandStatus = QueryMessagesCommandStatus(context, messageIds);
            return commandStatus.DisabledIfNot(condition);
        }

        [CommandStatusSubscriber("Janus.Forum.SetMessagesRepliesAutoReadMark")]
        public IDisposable SubscribeAutoReadMarkCommandStatusChanged(
            IServiceProvider provider, Action handler)
        {
            Action<ForumEntryChangedEventArgs> onNext = args =>
                                {
                                    if ((args.ChangeType & ForumEntryChangeType.AutoReadMark) != 0)
                                        handler();
                                };

            return provider.GetRequiredService<IEventBroker>().Subscribe(
                ForumEventNames.AfterForumEntryChanged,
                Observer.Create<ForumEntryChangedEventArgs>(onNext));
        }


        [CommandStatusSubscriber("Janus.Forum.AddForumMessageToFavorites")]
        [CommandStatusSubscriber("Janus.Forum.CopyMessageAddress")]
        [CommandStatusSubscriber("Janus.Forum.CopyMessageAuthorAddress")]
        [CommandStatusSubscriber("Janus.Forum.OpenMessageInJBrowser")]
        [CommandStatusSubscriber("Janus.Forum.OpenMessageOnRsdn")]
        [CommandStatusSubscriber("Janus.Forum.OpenMessageRatingOnRsdn")]
        [CommandStatusSubscriber("Janus.Forum.Moderating")]
        [CommandStatusSubscriber("Janus.Forum.OpenModeratingOnRsdn")]
        [CommandStatusSubscriber("Janus.Forum.ShowMessageRating")]
        [CommandStatusSubscriber("Janus.Forum.ReplyMessage")]
        [CommandStatusSubscriber("Janus.Forum.RateMessage")]
        [CommandStatusSubscriber("Janus.Forum.RegetTopic")]
        [CommandStatusSubscriber("Janus.Forum.SetTopicReadMark")]
        [CommandStatusSubscriber("Janus.Forum.SetMessagesReadMark")]
        [CommandStatusSubscriber("Janus.Forum.SetMessagesRepliesAutoReadMark")]
        [CommandStatusSubscriber("Janus.Forum.MessageSelectAll")]
        public IDisposable SubscribeMessageCommandStatusChanged(
            IServiceProvider serviceProvider, Action handler)
        {
            return ForumMessageCommandHelper.SubscribeMessageCommandStatusChanged(serviceProvider, handler);
        }

        [CommandExecutor("Janus.Forum.MessageSelectAll")]
        public void ExecuteMessagesSelectAll(
            ICommandContext context, int[] messageIds)
        {
            IUIShell uiShell = context.GetService<IUIShell>();
            if (uiShell != null && uiShell.ActiveGrid != null)
            {
                Rsdn.TreeGrid.TreeGrid activeGrid = uiShell.ActiveGrid as TreeGrid.TreeGrid;
                if (activeGrid != null)
                {
                    activeGrid.SelectAll();

                    // ---update commands state

                    // update SelectedNodes first
                    ForumMessageCommandHelper.GetMessages(context, messageIds);

                    // then imitate message changing
                    IActiveMessagesService activeMessagesService = context.GetService<IActiveMessagesService>();
                    if (activeMessagesService != null)
                    {
                        activeMessagesService.UpdateMessageState();
                    }
                }
                else
                {
                    ListView grid = uiShell.ActiveGrid;
                    for (int i = 0; i < grid.Items.Count; i++)
                    {
                        ListViewItem item = grid.Items[i];
                        item.Selected = true;
                    }
                }
            }
        }

        [CommandStatusGetter("Janus.Forum.MessageSelectAll")]
        public CommandStatus QuerySetMessagesSelectAll(
            ICommandContext context, int[] messageIds)
        {
            CommandStatus commandStatus = ForumMessageCommandHelper.GetMultipleMessagesCommandStatus(context, messageIds);
            return commandStatus;
        }
    }
}